package com.xiyuan.smartutils;


import com.xiyuan.smartutils.reflect.Unsafe;

import java.io.Serializable;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import static com.xiyuan.smartutils.constants.TypeConstants.*;

/**
 * 基本类型相关工具类
 *
 * @version v1.0.0 @author lgz 2017-9-21 新建与整理
 */

public final class Types {
    static {
        Unsafe.init();
    }
    
    static final Type[] EMPTY_TYPE_ARRAY = new Type[]{};
    static final Map<String, Integer> clzHashMap = new ConcurrentHashMap<>();
    
    private Types() {}
    
    /************************************************************/
    // 类判断
    /************************************************************/
    
    /**
     * 是否八种基本类型一级包装类，
     * 支持byte/short/int/long/float/double/boolean/char和对应的类包装类
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isPrimitive(Class<?> clazz) {
        if (clazz == null) {return false;}
        if (clazz.isPrimitive()) return true;
        if (clazz == Boolean.class) return true;
        if (clazz == Character.class) return true;
        if (clazz == Integer.class) return true;
        if (clazz == Long.class) return true;
        if (clazz == Float.class) return true;
        if (clazz == Double.class) return true;
        if (clazz == Byte.class) return true;
        if (clazz == Short.class) return true;
        return clazz == Void.class;
    }
    
    /**
     * 是否八种基本原型基本类型，仅支持byte/short/int/long/float/double/boolean/char
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isPrimitiveBase(Class<?> clazz) {
        return clazz != null && clazz.isPrimitive() && clazz != void.class;
    }
    
    /**
     * 把八种基本类型转为对象类型 包括void 类型
     *
     * @param clazz 类结构
     * @return 返回对象类型，如int.class转为Integer.class
     */
    public static Class<?> getWrapType(Class<?> clazz) {
        if (!clazz.isPrimitive()) return clazz;
        if (clazz == void.class) return clazz;
        if (clazz == boolean.class) return Boolean.class;
        if (clazz == byte.class) return Byte.class;
        if (clazz == char.class) return Character.class;
        if (clazz == short.class) return Short.class;
        if (clazz == int.class) return Integer.class;
        if (clazz == long.class) return Long.class;
        if (clazz == float.class) return Float.class;
        if (clazz == double.class) return Double.class;
        return clazz;
    }
    
    /**
     * 是否数值型(整数/小数)，支持byte/short/int/long/float/double
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isNumber(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        if (clazz.isPrimitive()) {
            return clazz != void.class && clazz != char.class && clazz != boolean.class;
        }
        return Number.class.isAssignableFrom(clazz);
        
    }
    
    /**
     * 是否整数，区别于isInt，支持byte/short/int/long
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isInteger(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        if (clazz.isPrimitive()) {
            return byte.class == clazz || short.class == clazz || int.class == clazz || long.class == clazz;
        }
        
        return Byte.class == clazz || Short.class == clazz || Integer.class == clazz || Long.class == clazz;
    }
    
    /**
     * 是否小数，float和double
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isDecimal(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        
        if (clazz == float.class || clazz == Float.class) {
            return true;
        }
        else {
            return clazz == double.class || clazz == Double.class;
        }
    }
    
    /**
     * 是否int，int/Integer
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isInt(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        
        return clazz == int.class || clazz == Integer.class;
    }
    
    /**
     * 是否long，long/Long
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isLong(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        
        return clazz == long.class || clazz == Long.class;
    }
    
    /**
     * 是否short，short/Short
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isShort(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        
        return clazz == short.class || clazz == Short.class;
    }
    
    /**
     * 是否char，char/Character
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isChar(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        
        return clazz == char.class || clazz == Character.class;
    }
    
    /**
     * 是否byte，byte/Byte
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isByte(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        
        return clazz == byte.class || clazz == Byte.class;
    }
    
    /**
     * 是否float，float/Float
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isFloat(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        
        return clazz == float.class || clazz == Float.class;
    }
    
    /**
     * 是否double，double/Double
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isDouble(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        
        return clazz == double.class || clazz == Double.class;
    }
    
    /**
     * 是否boolean，boolean/Boolean
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isBoolean(Class<?> clazz) {
        return (clazz == boolean.class || clazz == Boolean.class);
    }
    
    /************************************************************/
    // 对象判断
    /************************************************************/
    
    /**
     * 是否单一类型，支持null/byte/short/int/long/float/double/boolean/char/String
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isSimpleType(Object obj) {
        if (obj == null) {
            return true;
        }
        
        Class<?> cls = obj.getClass();
        return isPrimitive(cls) || isInt(cls) || isLong(cls) || isBoolean(cls) || isByte(cls) || isShort(cls) || isChar(cls) || isFloat(cls) || isDouble(cls) || isString(obj);
        
    }
    
    /**
     * 是否八种基本类型，支持byte/short/int/long/float/double/boolean/char
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isPrimitive(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj instanceof Class) {
            return isPrimitive((Class<?>) obj);
        }
        
        if (isInt(obj)) {
            return true;
        }
        else if (isLong(obj)) {
            return true;
        }
        else if (isBoolean(obj)) {
            return true;
        }
        else if (isByte(obj)) {
            return true;
        }
        else if (isShort(obj)) {
            return true;
        }
        else if (isChar(obj)) {
            return true;
        }
        else if (isFloat(obj)) {
            return true;
        }
        else {
            return isDouble(obj);
        }
    }
    
    /**
     * 是否数值型(整数/小数)，支持byte/short/int/long/float/double
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isNumber(Object obj) {
        if (isInt(obj)) {
            return true;
        }
        else if (isLong(obj)) {
            return true;
        }
        else if (isByte(obj)) {
            return true;
        }
        else if (isShort(obj)) {
            return true;
        }
        else if (isFloat(obj)) {
            return true;
        }
        else {
            return isDouble(obj);
        }
    }
    
    /**
     * 是否整数，区别于isInt，支持byte/short/int/long
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isInteger(Object obj) {
        if (obj == null) {
            return false;
        }
        
        if (isInt(obj)) {
            return true;
        }
        else if (isLong(obj)) {
            return true;
        }
        else if (isByte(obj)) {
            return true;
        }
        else {
            return isShort(obj);
        }
    }
    
    /**
     * 是否小数，支持float/double
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isDecimal(Object obj) {
        if (obj == null) {
            return false;
        }
        
        if (isFloat(obj)) {
            return true;
        }
        else {
            return isDouble(obj);
        }
    }
    
    /**
     * 是否为null
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isNull(Object obj) {
        return (obj == null);
    }
    
    /**
     * 是否为字符串
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isString(Object obj) {
        return (obj instanceof String);
    }
    
    /**
     * 是否为int/Integer
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isInt(Object obj) {
        return (obj instanceof Integer);
    }
    
    /**
     * 是否为long/Long
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isLong(Object obj) {
        return (obj instanceof Long);
    }
    
    /**
     * 是否为short/Short
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isShort(Object obj) {
        return (obj instanceof Short);
    }
    
    /**
     * 是否为byte/Byte
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isByte(Object obj) {
        return (obj instanceof Byte);
    }
    
    /**
     * 是否为char/Character
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isChar(Object obj) {
        return (obj instanceof Character);
    }
    
    /**
     * 是否为boolean/Boolean
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isBoolean(Object obj) {
        return (obj instanceof Boolean);
    }
    
    /**
     * 是否为float/Float
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isFloat(Object obj) {
        return (obj instanceof Float);
    }
    
    /**
     * 是否为double/Double
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isDouble(Object obj) {
        return (obj instanceof Double);
    }
    
    /************************************************************/
    // 对象数组判断
    /************************************************************/
    
    /**
     * 是否为数组
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isArray(Object obj) {
        return obj != null && obj.getClass().isArray();
    }
    
    /**
     * 是否为数组
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isArray(Class<?> clazz) {
        return clazz != null && clazz.isArray();
    }
    
    /**
     * 是否8种基本类型对象数组，支持byte/short/int/long/float/double/boolean/char
     * <p>
     * 这里只判断类int[],不去判断Integer[]，因为Integer[]可转化为Object[]，而int[]不行
     *
     * @param clazz 类结构
     * @return =true/=false
     */
    public static boolean isPrimitiveArray(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        
        if (clazz == int[].class) {
            return true;
        }
        else if (clazz == long[].class) {
            return true;
        }
        else if (clazz == boolean[].class) {
            return true;
        }
        else if (clazz == byte[].class) {
            return true;
        }
        else if (clazz == char[].class) {
            return true;
        }
        else if (clazz == short[].class) {
            return true;
        }
        else if (clazz == float[].class) {
            return true;
        }
        else {
            return clazz == double[].class;
        }
    }
    
    /**
     * 是否8种基本类型对象数组，支持byte/short/int/long/float/double/boolean/char
     * <p>
     * 这里只判断类int[],不去判断Integer[]，因为Integer[]可转化为Object[]，而int[]不行
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isPrimitiveArray(Object obj) {
        return obj != null && isPrimitiveArray(obj.getClass());
    }
    
    /**
     * 是否为int[]，注意Integer[]不是int[]
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isIntArray(Object obj) {
        return obj != null && obj.getClass() == int[].class;
    }
    
    /**
     * 是否为long[]，注意Long[]不是long[]
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isLongArray(Object obj) {
        return obj != null && obj.getClass() == long[].class;
    }
    
    /**
     * 是否为short[]，注意Short[]不是short[]
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isShortArray(Object obj) {
        return obj != null && obj.getClass() == short[].class;
    }
    
    /**
     * 是否为byte[]，注意Byte[]不是byte[]
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isByteArray(Object obj) {
        return obj != null && obj.getClass() == byte[].class;
    }
    
    /**
     * 是否为char[]，注意Character[]不是char[]
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isCharArray(Object obj) {
        return obj != null && obj.getClass() == char[].class;
    }
    
    /**
     * 是否为float[]，注意Float[]不是float[]
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isFloatArray(Object obj) {
        return obj != null && obj.getClass() == float[].class;
    }
    
    /**
     * 是否为double[]，注意Double[]不是double[]
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isDoubleArray(Object obj) {
        return obj != null && obj.getClass() == double[].class;
    }
    
    /**
     * 是否为boolean[]，注意Boolean[]不是boolean[]
     *
     * @param obj 对象
     * @return =true/=false
     */
    public static boolean isBooleanArray(Object obj) {
        return obj != null && obj.getClass() == boolean[].class;
    }
    
    static void checkArgument(boolean condition) {
        if (!condition) {
            throw new IllegalArgumentException();
        }
    }
    
    static void checkNotPrimitive(Type type) {
        checkArgument(!(type instanceof Class<?>) || !((Class<?>) type).isPrimitive());
    }
    
    static int hashCodeOrZero(Object o) {
        return o != null ? o.hashCode() : 0;
    }
    
    static boolean equal(Object a, Object b) {
        return Objects.equals(a, b);
    }
    
    public static String typeToString(Type type) {
        return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
    }
    
    public static boolean equals(Type a, Type b) {
        if (a == b) {
            return true;
        }
        else if (a instanceof Class) {
            // Class already specifies equals().
            return a.equals(b);
        }
        else if (a instanceof ParameterizedType) {
            if (!(b instanceof ParameterizedType)) {
                return false;
            }
            
            // TODO: save a .clone() call
            ParameterizedType pa = (ParameterizedType) a;
            ParameterizedType pb = (ParameterizedType) b;
            return equal(pa.getOwnerType(), pb.getOwnerType()) && pa.getRawType()
                    .equals(pb.getRawType()) && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
        }
        else if (a instanceof GenericArrayType) {
            if (!(b instanceof GenericArrayType)) {
                return false;
            }
            
            GenericArrayType ga = (GenericArrayType) a;
            GenericArrayType gb = (GenericArrayType) b;
            return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
        }
        else if (a instanceof WildcardType) {
            if (!(b instanceof WildcardType)) {
                return false;
            }
            
            WildcardType wa = (WildcardType) a;
            WildcardType wb = (WildcardType) b;
            return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) && Arrays.equals(wa.getLowerBounds(),
                                                                                            wb.getLowerBounds());
        }
        else if (a instanceof TypeVariable) {
            if (!(b instanceof TypeVariable)) {
                return false;
            }
            TypeVariable<?> va = (TypeVariable<?>) a;
            TypeVariable<?> vb = (TypeVariable<?>) b;
            return va.getGenericDeclaration() == vb.getGenericDeclaration() && va.getName().equals(vb.getName());
        }
        else {
            // This isn't a type we support. Could be a generic array type, wildcard type, etc.
            return false;
        }
    }
    
    public static Type canonicalize(Type type) {
        if (type instanceof Class) {
            Class<?> c = (Class<?>) type;
            return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
        }
        else if (type instanceof ParameterizedType) {
            ParameterizedType p = (ParameterizedType) type;
            return new ParameterizedTypeImpl(p.getOwnerType(), p.getRawType(), p.getActualTypeArguments());
        }
        else if (type instanceof GenericArrayType) {
            GenericArrayType g = (GenericArrayType) type;
            return new GenericArrayTypeImpl(g.getGenericComponentType());
        }
        else if (type instanceof WildcardType) {
            WildcardType w = (WildcardType) type;
            return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
        }
        else {
            // type is either serializable as-is or unsupported
            return type;
        }
    }
    
    public static Class<?> getRawType(Type type) {
        if (type instanceof Class<?>) {
            // type is a normal class.
            return (Class<?>) type;
        }
        else if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            // I'm not exactly sure why getRawType() returns Type instead of Class.
            // Neal isn't either but suspects some pathological case related
            // to nested classes exists.
            Type rawType = parameterizedType.getRawType();
            checkArgument(rawType instanceof Class);
            return (Class<?>) rawType;
        }
        else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            return Array.newInstance(getRawType(componentType), 0).getClass();
        }
        else if (type instanceof TypeVariable) {
            // we could use the variable's bounds, but that won't work if there are multiple.
            // having a raw type that's more general than necessary is okay
            return Object.class;
        }
        else if (type instanceof WildcardType) {
            return getRawType(((WildcardType) type).getUpperBounds()[0]);
        }
        else {
            String className = type == null ? "null" : type.getClass().getName();
            throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " + "GenericArrayType, but <" + type + "> is of type " + className);
        }
    }
    
    
    static final class ParameterizedTypeImpl implements ParameterizedType, Serializable {
        private final Type ownerType;
        private final Type rawType;
        private final Type[] typeArguments;
        
        public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
            // require an owner type if the raw type needs it
            if (rawType instanceof Class<?>) {
                Class<?> rawTypeAsClass = (Class<?>) rawType;
                boolean isStaticOrTopLevelClass = Modifier.isStatic(rawTypeAsClass.getModifiers()) || rawTypeAsClass.getEnclosingClass() == null;
                checkArgument(ownerType != null || isStaticOrTopLevelClass);
            }
            
            this.ownerType = ownerType == null ? null : canonicalize(ownerType);
            this.rawType = canonicalize(rawType);
            this.typeArguments = typeArguments.clone();
            for (int t = 0, length = this.typeArguments.length; t < length; t++) {
                checkNotPrimitive(this.typeArguments[t]);
                this.typeArguments[t] = canonicalize(this.typeArguments[t]);
            }
        }
        
        @Override
        public Type[] getActualTypeArguments() {
            return typeArguments.clone();
        }
        
        @Override
        public Type getRawType() {
            return rawType;
        }
        
        @Override
        public Type getOwnerType() {
            return ownerType;
        }
        
        @Override
        public boolean equals(Object other) {
            return other instanceof ParameterizedType && Types.equals(this, (ParameterizedType) other);
        }
        
        @Override
        public int hashCode() {
            return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
        }
        
        @Override
        public String toString() {
            int length = typeArguments.length;
            if (length == 0) {
                return typeToString(rawType);
            }
            
            StringBuilder stringBuilder = new StringBuilder(30 * (length + 1));
            stringBuilder.append(typeToString(rawType)).append("<").append(typeToString(typeArguments[0]));
            for (int i = 1; i < length; i++) {
                stringBuilder.append(", ").append(typeToString(typeArguments[i]));
            }
            return stringBuilder.append(">").toString();
        }
        
        private static final long serialVersionUID = 0;
    }
    
    public static final class GenericArrayTypeImpl implements GenericArrayType, Serializable {
        private final Type componentType;
        
        public GenericArrayTypeImpl(Type componentType) {
            this.componentType = canonicalize(componentType);
        }
        
        @Override
        public Type getGenericComponentType() {
            return componentType;
        }
        
        @Override
        public boolean equals(Object o) {
            return o instanceof GenericArrayType && Types.equals(this, (GenericArrayType) o);
        }
        
        @Override
        public int hashCode() {
            return componentType.hashCode();
        }
        
        @Override
        public String toString() {
            return typeToString(componentType) + "[]";
        }
        
        private static final long serialVersionUID = 0;
    }
    
    /**
     * The WildcardType interface supports multiple upper bounds and multiple
     * lower bounds. We only support what the Java 6 language needs - at most one
     * bound. If a lower bound is set, the upper bound must be Object.class.
     */
    static final class WildcardTypeImpl implements WildcardType, Serializable {
        private final Type upperBound;
        private final Type lowerBound;
        
        public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
            checkArgument(lowerBounds.length <= 1);
            checkArgument(upperBounds.length == 1);
            
            if (lowerBounds.length == 1) {
                checkNotPrimitive(lowerBounds[0]);
                checkArgument(upperBounds[0] == Object.class);
                this.lowerBound = canonicalize(lowerBounds[0]);
                this.upperBound = Object.class;
            }
            else {
                checkNotPrimitive(upperBounds[0]);
                this.lowerBound = null;
                this.upperBound = canonicalize(upperBounds[0]);
            }
        }
        
        @Override
        public Type[] getUpperBounds() {
            return new Type[]{upperBound};
        }
        
        @Override
        public Type[] getLowerBounds() {
            return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY;
        }
        
        @Override
        public boolean equals(Object other) {
            return other instanceof WildcardType && Types.equals(this, (WildcardType) other);
        }
        
        @Override
        public int hashCode() {
            // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
            return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
        }
        
        @Override
        public String toString() {
            if (lowerBound != null) {
                return "? super " + typeToString(lowerBound);
            }
            else if (upperBound == Object.class) {
                return "?";
            }
            else {
                return "? extends " + typeToString(upperBound);
            }
        }
        
        private static final long serialVersionUID = 0;
    }
    
    /**
     * 获取类型结构的哈希码
     *
     * @param cls 类结构，
     * @return hash code
     */
    public static int getClassHashCode(Class<?> cls) {
        String name = cls == null ? null : cls.getCanonicalName();
        if (cls == null) {return -1;}
        
        // 缓存哈希值
        Integer h = clzHashMap.get(name);
        if (h != null) {return h;}
        int length = name.length();
        h = 0;
        for (int i = 0; i < length; i++) {h = 31 * h + name.charAt(i);}
        clzHashMap.put(name, h);
        return h;
    }
}
